/*
 * Copyright (C) 2010 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.org.manhu.chargehelper.Zxing.camera;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

import android.content.Context;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;

final class CameraConfigurationManager
{

    private static final String TAG = CameraConfigurationManager.class
            .getSimpleName();

    private static final int TEN_DESIRED_ZOOM = 27;
    // private static final int DESIRED_SHARPNESS = 30;

    /**
     * 用来切割尺寸的字符串，里面的尺寸是用  , 分开的
     */
    private static final Pattern COMMA_PATTERN = Pattern.compile(",");

    private final Context context;
    private Point screenResolution;
    private Point cameraResolution;
    private int previewFormat;
    private String previewFormatString;

    CameraConfigurationManager(Context context)
    {
        this.context = context;
    }

    /**
     * Reads, one time, values from the camera that are needed by the app.
     */
    void initFromCameraParameters(Camera camera)
    {
        Camera.Parameters parameters = camera.getParameters();
        previewFormat = parameters.getPreviewFormat();
        previewFormatString = parameters.get("preview-format");
        Log.d(TAG, "Default preview format: " + previewFormat + '/'
                + previewFormatString);
        WindowManager manager = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = manager.getDefaultDisplay();
        screenResolution = new Point(display.getWidth(), display.getHeight());
        Log.d(TAG, "Screen resolution: " + screenResolution);
        Point screenResolutionForCamera = new Point();
        screenResolutionForCamera.x = screenResolution.x;
        screenResolutionForCamera.y = screenResolution.y;
        // preview size is always something like 480*320, other 320*480
        if (screenResolution.x < screenResolution.y) {
        	screenResolutionForCamera.x = screenResolution.y;
        	screenResolutionForCamera.y = screenResolution.x;
        }
        cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
        Log.d(TAG, "Camera resolution: " + cameraResolution);
    }

    /**
     * Sets the camera up to take preview images which are used for both preview
     * and decoding. We detect the preview format here so that
     * buildLuminanceSource() can build an appropriate LuminanceSource subclass.
     * In the future we may want to force YUV420SP as it's the smallest, and the
     * planar Y can be used for barcode scanning without a copy in some cases.
     */
    void setDesiredCameraParameters(Camera camera)
    {
        Camera.Parameters parameters = camera.getParameters();
        Log.d(TAG, "Setting preview size: " + cameraResolution);
        parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
        setFlash(parameters);
        setZoom(parameters);
        // setSharpness(parameters);
        camera.setParameters(parameters);
        setDisplayOrientation(camera, 90); 
    }
    private void setDisplayOrientation(Camera camera, int angle) {
        Method downPolymorphic;
        try {
            downPolymorphic = camera.getClass().getMethod(
                    "setDisplayOrientation", new Class[] { int.class });
            if (downPolymorphic != null)
                downPolymorphic.invoke(camera, new Object[] { angle });
        } catch (Exception e1) {
        }
    }

    Point getCameraResolution()
    {
        return cameraResolution;
    }

    Point getScreenResolution()
    {
        return screenResolution;
    }

    int getPreviewFormat()
    {
        return previewFormat;
    }

    String getPreviewFormatString()
    {
        return previewFormatString;
    }

    private static Point getCameraResolution(Camera.Parameters parameters,
            Point screenResolution)
    {

        String previewSizeValueString = parameters.get("preview-size-values");
        // saw this on Xperia
        if (previewSizeValueString == null)
        {
            previewSizeValueString = parameters.get("preview-size-value");
        }

        Point cameraResolution = null;

        if (previewSizeValueString != null)
        {
            /**
             * previewSizeValueString:
             * 1280x720,864x480,640x480,480x320,352x288,320x240,176x144
             */
            Log.d(TAG, "preview-size-values parameter: "
                    + previewSizeValueString);
            cameraResolution = findBestPreviewSizeValue(previewSizeValueString,
                    screenResolution);
        }

        if (cameraResolution == null)
        {
            // Ensure that the camera resolution is a multiple of 8, as the
            // screen may not be.
            /**
             * 确保cameraResolution是8的倍数，除8再乘8
             */
            cameraResolution = new Point((screenResolution.x >> 3) << 3,
                    (screenResolution.y >> 3) << 3);
        }

        return cameraResolution;
    }

    private static Point findBestPreviewSizeValue(
            CharSequence previewSizeValueString, Point screenResolution)
    {
        int bestX = 0;
        int bestY = 0;
        int diff = Integer.MAX_VALUE;
        for (String previewSize : COMMA_PATTERN.split(previewSizeValueString))
        {
            /**
             * 1280x720,864x480,640x480,480x320,352x288,320x240,176x144
             */
            previewSize = previewSize.trim();
            int dimPosition = previewSize.indexOf('x');
            if (dimPosition < 0)
            {
                Log.w(TAG, "Bad preview-size: " + previewSize);
                continue;
            }

            int newX;
            int newY;
            try
            {
                /**
                 * 根据x位置得到尺寸
                 */
                newX = Integer.parseInt(previewSize.substring(0, dimPosition));
                newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
            }
            catch (NumberFormatException nfe)
            {
                Log.w(TAG, "Bad preview-size: " + previewSize);
                continue;
            }

            int newDiff = Math.abs(newX - screenResolution.x)
                    + Math.abs(newY - screenResolution.y);
            /**
             * 如果相机参数中获得的尺寸与屏幕尺寸相等，那么这个尺寸就是合适的尺寸
             */
            if (newDiff == 0)
            {
                bestX = newX;
                bestY = newY;
                break;
            }
            else if (newDiff < diff)
            {
                /**
                 * 否则差距最小的那个就是最合适的尺寸
                 */
                bestX = newX;
                bestY = newY;
                diff = newDiff;
            }

        }

        if (bestX > 0 && bestY > 0)
        {
            return new Point(bestX, bestY);
        }
        return null;
    }

    /**
     * 如果mot-zoom-values为空，这个方法没有用到就
     */
    private static int findBestMotZoomValue(CharSequence stringValues,
            int tenDesiredZoom)
    {
        int tenBestValue = 0;
        for (String stringValue : COMMA_PATTERN.split(stringValues))
        {
            stringValue = stringValue.trim();
            double value;
            try
            {
                value = Double.parseDouble(stringValue);
            }
            catch (NumberFormatException nfe)
            {
                return tenDesiredZoom;
            }
            int tenValue = (int) (10.0 * value);
            if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom
                    - tenBestValue))
            {
                tenBestValue = tenValue;
            }
        }
        return tenBestValue;
    }

    private void setFlash(Camera.Parameters parameters)
    {
        // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
        // And this is a hack-hack to work around a different value on the
        // Behold II
        // Restrict Behold II check to Cupcake, per Samsung's advice
        // if (Build.MODEL.contains("Behold II") &&
        // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {
        if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3)
        { // 3 = Cupcake
            parameters.set("flash-value", 1);
        }
        else
        {
            parameters.set("flash-value", 2);
        }
        // This is the standard setting to turn the flash off that all devices
        // should honor.
        parameters.set("flash-mode", "off");
    }

    private void setZoom(Camera.Parameters parameters)
    {

        String zoomSupportedString = parameters.get("zoom-supported");
        /**
         * zoom-supported:true
         */
        if (zoomSupportedString != null
                && !Boolean.parseBoolean(zoomSupportedString))
        {
            return;
        }

        int tenDesiredZoom = TEN_DESIRED_ZOOM;

        String maxZoomString = parameters.get("max-zoom");
        /**
         * max-zoom:40
         */
        if (maxZoomString != null)
        {
            try
            {
                int tenMaxZoom = (int) (10.0 * Double
                        .parseDouble(maxZoomString));
                if (tenDesiredZoom > tenMaxZoom)
                {
                    tenDesiredZoom = tenMaxZoom;
                }
            }
            catch (NumberFormatException nfe)
            {
                Log.w(TAG, "Bad max-zoom: " + maxZoomString);
            }
        }

        String takingPictureZoomMaxString = parameters
                .get("taking-picture-zoom-max");
        /**
         * taking-picture-zoom-max:null
         */
        Log.i("MSG", "taking-picture-zoom-max:"+takingPictureZoomMaxString);
        if (takingPictureZoomMaxString != null)
        {
            try
            {
                int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
                if (tenDesiredZoom > tenMaxZoom)
                {
                    tenDesiredZoom = tenMaxZoom;
                }
            }
            catch (NumberFormatException nfe)
            {
                Log.w(TAG, "Bad taking-picture-zoom-max: "
                        + takingPictureZoomMaxString);
            }
        }

        String motZoomValuesString = parameters.get("mot-zoom-values");
        /**
         * mot-zoom-values:null
         */
        if (motZoomValuesString != null)
        {
            tenDesiredZoom = findBestMotZoomValue(motZoomValuesString,
                    tenDesiredZoom);
        }

        String motZoomStepString = parameters.get("mot-zoom-step");
        /**
         * mot-zoom-step:null
         */
        if (motZoomStepString != null)
        {
            try
            {
                double motZoomStep = Double.parseDouble(motZoomStepString
                        .trim());
                int tenZoomStep = (int) (10.0 * motZoomStep);
                if (tenZoomStep > 1)
                {
                    tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
                }
            }
            catch (NumberFormatException nfe)
            {
                // continue
            }
        }

        // Set zoom. This helps encourage the user to pull back.
        // Some devices like the Behold have a zoom parameter
        if (maxZoomString != null || motZoomValuesString != null)
        {
            parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
        }

        // Most devices, like the Hero, appear to expose this zoom parameter.
        // It takes on values like "27" which appears to mean 2.7x zoom
        if (takingPictureZoomMaxString != null)
        {
            parameters.set("taking-picture-zoom", tenDesiredZoom);
        }
    }
}
